iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 16
1
Software Development

30天完成家庭任務平台系列 第 16

30天完成家庭任務平台:第十六天

  • 分享至 

  • xImage
  •  

Controller系列中的權限控制

Controller中提供了對應的方法讓使用者對資源進行讀取/建立/更新/刪除等操作(以ProjectController為例):

ProjectController方法|HTTP方法|操作|URL
------------- | -------------| -------------
index|GET|讀取所有計畫資料|projects
show|GET|讀取一筆計畫資料|projects/{project}
store|POST|建立一筆計畫資料|projects
update|PUT/PATCH|更新一筆計畫資料|projects/{project}
destory|DELETE|刪除一筆計畫資料|projects/{project}

  • 家庭任務平台中是多個使用者,所以會希望確認使用者是有權限去讀取/建立/更新/刪除這些計畫,不會去干擾其他使用者,例如刪除掉其他使用者的計畫,因此我們需要做權限的控管。

控制權限有一些方法,介紹如下:

  1. 直接寫在Controller裡面:
    以TaskController的isUserOwnerOrMemberOfProject方法為例:
    protected function isUserOwnerOrMemberOfProject(Task $task)
    {
        if (
            Auth::user()->isNot($task->project->owner)
            && !$task->project->members->contains(Auth::user())
        ) {
            abort(403);
        }
    }

由於權限設定為計畫擁有者或者是被邀請的成員才能夠進到更新任務的頁面並更新任務,所以這個方法用來判斷目前登入的使用者是否是計畫擁有者或者是成員,如果不是,則會回傳403給使用者。

PS Laravel有提供一些方法可以判斷是否需要回傳403:abort/abort_if/abort_unless

  • Gate與Policy並不是互斥的,甚至很常混用,通常的使用場景為:
權限 使用情境 好處
Gate 不適用Policy 很有彈性,可以搭配Policy使用,也可以自定義權限規則
Policy Policy需要跟某個MODEL或某種資源連結在一起作權限管理 由於Policy會跟某個MODEL或資源連結,比起Gate可以集中對該MODEL的讀取/建立/更新/刪除等權限管理
  1. Policy
    Policy需要跟Model或資源連結在一起,所以建立Policy時,常見Policy會命名為其對應Model+Policy,例子如:php artisan make:policy ProjectPolicy -m Project。

(1) 在AuthServiceProvider註冊Policy:

class AuthServiceProvider extends ServiceProvider
{
    ...
    protected $policies = [
       Project::class => ProjectPolicy::class,
    ];
    ...
}

(2) 如果有加-m參數,則會在ProjectPolicy中自動創建以下方法(與Controller方法對應):

Controller方法 Policy方法
index viewAny
show view
create create
store create
edit update
update update
destroy delete

PS 如果不需要用到那麼多方法,可以不需要-m

(3) 制定規則
這是ProjectPolicy的權限

class ProjectPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view the model.
     *
     * @param  \App\User  $user
     * @param  \App\Project  $project
     * @return mixed
     */
    public function view(User $user, Project $project)
    {
        return $project->members->contains($user)|| $user->is($project->owner);
    }
    /**
     *
     * @param  \App\User  $user
     * @param  \App\Project  $project
     * @return mixed
     */
    public function invite(User $user,Project $project)
    {
        return $user->is($project->owner);
    }

    /**
     * Determine whether the user can update the model.
     *
     * @param  \App\User  $user
     * @param  \App\Project  $project
     * @return mixed
     */
    public function update(User $user, Project $project)
    {
        
        return $project->members->contains($user)|| $user->is($project->owner);
    }

    /**
     * Determine whether the user can delete the model.
     *
     * @param  \App\User  $user
     * @param  \App\Project  $project
     * @return mixed
     */
    public function delete(User $user, Project $project)
    {
        return $user->is($project->owner);
    }
    
}

  1. Gate

Gate可以在App\Providers\AuthServiceProvider中定義其權限,可以自定義,也可以使用Policy的方法重新命名,在沒有定義的情況下,會去取用PostPolicy的同名方法:


public function boot()
{
    $this->registerPolicies();
    //自定義
    Gate::define('update-project', function ($user, $project) {
        return $user->id === $project->user_id;
    });
    //使用Policy的方法
    Gate::define('update-project', [ProjectPolicy::class, 'update']);
}

Policy可搭配以下方法來管理權限:
(1) Controller中$this->authorize('update',$project)方法。
(2) 加掛Middleware在路徑上。
(3) User Model的can/cant方法。

由於Gate很有彈性,所以不是在上面情境中想控制權限時,我都會想到可以使用Gate。

以InvitationRequest為例,由於這授權規則不是寫在Controller中,所以不能使用$this->authorize()時,
則可以選擇:
(1) 加掛Middleware

Route::post('/projects/{project}/invitations', 'ProjectInvitationController@store')->name('invitation.store')->middleware('can:update,project')

(2)使用Model User

class InvitationRequest extends FormRequest
{
   ...
    public function authorize()
        {
            return Auth::user()->can('invite',$this->route('project')); 
        }
     ...
}

(3)使用Gate(自動確認ProjectPolicy的invite權限)

class InvitationRequest extends FormRequest
{
   ...
    public function authorize()
    {
        return Gate::allows('invite',$this->route('project'));
        
    }

   ...
}

明天則是讀Laravel的文件,並做一些關於權限的測試。


上一篇
30天完成家庭任務平台:第十五天
下一篇
30天完成家庭任務平台:第十七天
系列文
30天完成家庭任務平台30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言